
package com.ctrip.corp.obt.shard.sql.mybatis.interceptor.inner;

import com.ctrip.corp.obt.generic.core.tuple.Tuple;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.generic.utils.JsonUtils;
import com.ctrip.corp.obt.generic.utils.StringUtils;
import com.ctrip.corp.obt.metric.Metrics;
import com.ctrip.corp.obt.metric.spectator.api.Id;
import com.ctrip.corp.obt.shard.annotation.SecurityFuzzyQueryEntity;
import com.ctrip.corp.obt.shard.annotation.SecurityFuzzyQueryField;
import com.ctrip.corp.obt.shard.configuration.Config;
import com.ctrip.corp.obt.shard.sql.parser.AbstractSqlParserSupport;
import com.ctrip.corp.obt.shard.sql.parser.IPluginPropertyParserRequest;
import com.ctrip.corp.obt.shard.sql.parser.fuzzy.SecurityFuzzyQueryHandler;
import com.ctrip.corp.obt.shard.utils.ParameterPropertyUtils;
import com.ctrip.corp.obt.shard.utils.PluginUtils;
import com.ctrip.corp.obt.shard.utils.PluginUtils.DSBoundSql;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;

/**
 * 加密数据模糊查询 SQL InnerInterceptor
 *
 * @author xukang zhang
 * @since 2023-12-25
 */
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@Slf4j
@SuppressWarnings({"rawtypes"})
public class FuzzyQueryEncryptPlugin extends AbstractSqlParserSupport implements InnerPlugin {

    /**
     * 查询时支持进行解析的函数名
     */
    private static final Set<String> QUERY_FUNCTION_NAME_SET = new HashSet<>(Collections.singletonList("concat"));
    /**
     * 埋点前缀
     */
    private static final String FUZZY_QUERY_ENCRYPT_PREFIX = "arch.shard.security.fuzzyQuery.";
    /**
     * 埋点id
     */
    private static final Id FUZZY_QUERY_ENCRYPT_INSERT =
        Metrics.REGISTRY.createId(FUZZY_QUERY_ENCRYPT_PREFIX + ".insert");
    private static final Id FUZZY_QUERY_ENCRYPT_UPDATE =
        Metrics.REGISTRY.createId(FUZZY_QUERY_ENCRYPT_PREFIX + ".update");
    private static final Id FUZZY_QUERY_ENCRYPT_QUERY =
        Metrics.REGISTRY.createId(FUZZY_QUERY_ENCRYPT_PREFIX + ".query");

    private static final String FUZZY_QUERY_ENCRYPT_EXCEPTION = "exceptionName";

    private static final String UNKNOWN = "UNKNOWN";
    public static final String SKIP_THE_INVALID_ENCRYPTED_CONTENT = "Skip the invalid encryptedContent: {}";

    @Override
    public boolean skipPluginQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
        ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        if (parameter == null) {
            return true;
        }
        // 仅处理加了SecurityFuzzyScan注解的参数
        return parameter.getClass().getAnnotation(SecurityFuzzyQueryEntity.class) == null
            || !Config.defaultConfig().enabledQuery();
    }

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
        ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        DSBoundSql mpBs = PluginUtils.dsBoundSql(boundSql);
        FuzzyParserAdditionalRequest multiFuzzyParserAdditionalRequest =
            new FuzzyParserAdditionalRequest(mpBs, ms.getConfiguration());
        mpBs.sql(parserSingle(mpBs.sql(), multiFuzzyParserAdditionalRequest));
    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        PluginUtils.DSStatementHandler dsSh = PluginUtils.dsStatementHandler(sh);
        MappedStatement ms = dsSh.mappedStatement();
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE) {
            DSBoundSql mpBs = dsSh.dsBoundSql();
            if (mpBs == null) {
                return;
            }
            Object parameterObject = mpBs.parameterObject();
            if (parameterObject == null) {
                return;
            }
            // 仅处理加了SecurityFuzzyScan注解的参数
            if (parameterObject.getClass().getAnnotation(SecurityFuzzyQueryEntity.class) == null
                || !Config.defaultConfig().enabledCommand()) {
                return;
            }
            FuzzyParserAdditionalRequest multiFuzzyParserAdditionalRequest =
                new FuzzyParserAdditionalRequest(mpBs, dsSh.configuration());
            mpBs.sql(parserMulti(mpBs.sql(), multiFuzzyParserAdditionalRequest));
        }

    }

    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        Instant currentStartTime = Instant.now();
        if (!(obj instanceof FuzzyParserAdditionalRequest)) {
            return;
        }
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest = (FuzzyParserAdditionalRequest)obj;
        if (!IPluginPropertyParserRequest.validate(fuzzyParserAdditionalRequest)) {
            return;
        }

        try {
            processSelectBody(select.getSelectBody(), fuzzyParserAdditionalRequest);
            List<WithItem> withItemsList = select.getWithItemsList();
            if (!CollectionUtils.isEmpty(withItemsList)) {
                withItemsList.forEach(w -> processSelectBody(w, fuzzyParserAdditionalRequest));
            }
        } catch (Exception e) {
            Metrics.REGISTRY.timer(FUZZY_QUERY_ENCRYPT_QUERY
                .withTag(FUZZY_QUERY_ENCRYPT_EXCEPTION,
                    Optional.of(e.getClass()).map(Class::getSimpleName).orElse(UNKNOWN)))
                .record(getDuration(currentStartTime));
            return;
        }
        Metrics.REGISTRY.timer(FUZZY_QUERY_ENCRYPT_QUERY)
            .record(getDuration(currentStartTime));
    }

    /**
     * 遍历SelectBody 将like中需要模糊查询的字段替换成 加密的分词数据
     *
     * @param selectBody the body of select
     * @param request the additional request
     */
    private void processSelectBody(SelectBody selectBody, FuzzyParserAdditionalRequest request) {
        if (selectBody != null) {
            if (selectBody instanceof PlainSelect) {
                this.processPlainSelect((PlainSelect)selectBody, request);
            } else if (selectBody instanceof WithItem) {
                WithItem withItem = (WithItem)selectBody;
                this.processSelectBody(withItem.getSubSelect().getSelectBody(), request);
            } else {
                SetOperationList operationList = (SetOperationList)selectBody;
                List<SelectBody> selectBodys = operationList.getSelects();
                if (CollectionUtils.isNotEmpty(selectBodys)) {
                    selectBodys.forEach(s -> processSelectBody(s, request));
                }
            }

        }
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        Instant currentStartTime = Instant.now();
        List<Column> columns = insert.getColumns();
        if (CollectionUtils.isEmpty(columns)) {
            // 针对不给列名的insert 不处理
            return;
        }
        if (!(obj instanceof FuzzyParserAdditionalRequest)) {
            return;
        }
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest = (FuzzyParserAdditionalRequest)obj;
        if (!IPluginPropertyParserRequest.validate(fuzzyParserAdditionalRequest)) {
            return;
        }
        DSBoundSql boundSql = fuzzyParserAdditionalRequest.getBoundSql();
        if (boundSql == null) {
            return;
        }
        Object parameterObject = boundSql.parameterObject();
        List<ParameterMapping> parameterMappings = boundSql.parameterMappings();
        if (parameterObject == null || CollectionUtils.isEmpty(parameterMappings)) {
            return;
        }

        try {
            // 遍历列的取值，若有需要进行模糊查询的加密列，则添加对应的分词列数据信息
            ItemsList itemsList = insert.getItemsList();
            if (itemsList != null) {
                itemsList.accept(new ItemsListVisitor() {
                    @Override
                    public void visit(SubSelect subSelect) {
                        // empty method
                    }

                    @Override
                    public void visit(ExpressionList expressionList) {
                        processExpressionListForInsert(expressionList, parameterObject, parameterMappings, columns,
                            fuzzyParserAdditionalRequest);
                        // add columns at last
                        List<Column> delayedAddColumns = fuzzyParserAdditionalRequest.getDelayedAddColumns();
                        if (CollectionUtils.isNotEmpty(delayedAddColumns)) {
                            insert.getColumns().addAll(delayedAddColumns);
                        }
                    }

                    @Override
                    public void visit(NamedExpressionList namedExpressionList) {
                        // empty method
                    }

                    @Override
                    public void visit(MultiExpressionList multiExpressionList) {
                        List<ExpressionList> expressionLists = multiExpressionList.getExpressionLists();
                        if (CollectionUtils.isEmpty(expressionLists)) {
                            return;
                        }
                        // traverse each expressionList
                        expressionLists
                            .forEach(expressionList -> processExpressionListForInsert(expressionList, parameterObject,
                                parameterMappings, columns,
                                fuzzyParserAdditionalRequest));
                        // add columns at last
                        List<Column> delayedAddColumns = fuzzyParserAdditionalRequest.getDelayedAddColumns();
                        if (CollectionUtils.isNotEmpty(delayedAddColumns)) {
                            insert.getColumns().addAll(delayedAddColumns);
                        }
                    }
                });
            }
        } catch (Exception e) {
            Metrics.REGISTRY.timer(FUZZY_QUERY_ENCRYPT_INSERT
                .withTag(FUZZY_QUERY_ENCRYPT_EXCEPTION,
                    Optional.of(e.getClass()).map(Class::getSimpleName).orElse(UNKNOWN)))
                .record(getDuration(currentStartTime));
            return;
        }
        Metrics.REGISTRY.timer(FUZZY_QUERY_ENCRYPT_INSERT)
            .record(getDuration(currentStartTime));

    }

    /**
     * 处理ExpressionList:
     * 遍历列的取值，若有需要进行模糊查询的加密列，则添加对应的分词列数据信息
     *
     * @param expressionList the expression list
     * @param parameterObject the parameter object
     * @param parameterMappings the parameter mappings
     * @param columns the columns
     * @param fuzzyParserAdditionalRequest the additional request
     */
    private void processExpressionListForInsert(ExpressionList expressionList, Object parameterObject,
        List<ParameterMapping> parameterMappings, List<Column> columns,
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        List<Expression> expressions = expressionList.getExpressions();
        if (CollectionUtils.isEmpty(expressions)) {
            return;
        }
        if (expressions.size() != columns.size()) {
            log.warn("The itemsList size[{}] is not equal to columns size[{}]", expressions.size(),
                columns.size());
            return;
        }
        // 需要添加的分词列信息
        List<AddColumnInfo> addColumnInfos = new ArrayList<>();
        for (int i = 0; i < expressions.size(); i++) {
            Expression expression = expressions.get(i);
            if (!(expression instanceof JdbcParameter)) {
                // 仅处理 预编译参数
                continue;
            }
            JdbcParameter jdbcParameter = (JdbcParameter)expression;
            Integer parameterIndex = jdbcParameter.getIndex();
            if (parameterIndex == null) {
                continue;
            }
            ParameterMapping parameterMapping = parameterMappings.get(parameterIndex - 1);
            String propertyName = parameterMapping.getProperty();

            // 判断是否需要处理
            Field propertyLocatedField = ParameterPropertyUtils.getPropertyLocatedField(fuzzyParserAdditionalRequest,
                parameterMapping, parameterObject);
            if (propertyLocatedField == null) {
                // 无Field信息
                continue;
            }
            SecurityFuzzyQueryField securityFuzzyQueryField =
                propertyLocatedField.getAnnotation(SecurityFuzzyQueryField.class);
            if (securityFuzzyQueryField == null) {
                // 跳过无SecurityFuzzyQueryField注解的属性
                continue;
            }
            // 获取字段取值
            String content = SecurityFuzzyQueryHandler.buildQueryContent(propertyName, parameterMapping,
                parameterObject, fuzzyParserAdditionalRequest);
            if (StringUtils.isBlank(content)) {
                continue;
            }

            // 分词加密
            String encryptAlgorithm = securityFuzzyQueryField.encryptAlgorithm();
            int divideLength = securityFuzzyQueryField.divideLength();
            String encryptedContent =
                SecurityFuzzyQueryHandler.encryptWithDividing(content, encryptAlgorithm, divideLength);

            if (StringUtils.isBlank(encryptedContent) || content.equals(encryptedContent)) {
                // 跳过加密失败数据的模糊查询条件
                log.warn(SKIP_THE_INVALID_ENCRYPTED_CONTENT, encryptedContent);
                return;
            }

            // 维护新增列的相关信息
            AddColumnInfo addColumnInfo = new AddColumnInfo();
            addColumnInfo.setContent(encryptedContent);
            addColumnInfo.setParameterMappingIndex(parameterIndex);
            addColumnInfo.setColumnName(
                SecurityFuzzyQueryHandler.buildDivideColumnName(securityFuzzyQueryField, columns.get(i)));
            addColumnInfos.add(addColumnInfo);
        }
        // 插入 分词列数据
        if (CollectionUtils.isNotEmpty(addColumnInfos)) {
            log.info("Insert secret_divide columns for fuzzy query, {}",
                JsonUtils.toJsonString(addColumnInfos));
            List<Column> delayedAddColumns = fuzzyParserAdditionalRequest.getDelayedAddColumns();
            for (AddColumnInfo addColumnInfo : addColumnInfos) {
                Column newColumn = new Column(addColumnInfo.columnName);
                if (delayedAddColumns.stream()
                    .noneMatch(c -> sameColumn(c, newColumn))) {
                    // 插入列名
                    delayedAddColumns.add(newColumn);
                }
                // 插入列取值
                expressions.add(new StringValue(addColumnInfo.getContent()));
            }
        }
    }

    /**
     * 判断两个列是否相同
     *
     * @param currentColumn the current column
     * @param newColumn the new column
     * @return whether is the same column
     */
    private static boolean sameColumn(Column currentColumn, Column newColumn) {
        return Objects.equals(currentColumn.getColumnName(), newColumn.getColumnName()) && Objects.equals(
            Optional.ofNullable(currentColumn.getTable()).map(Table::getName).orElse(StringUtils.EMPTY),
            Optional.ofNullable(newColumn.getTable()).map(Table::getName).orElse(StringUtils.EMPTY));
    }

    /**
     * update 语句处理
     */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        Instant currentStartTime = Instant.now();
        if (!(obj instanceof FuzzyParserAdditionalRequest)) {
            return;
        }
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest = (FuzzyParserAdditionalRequest)obj;
        if (!IPluginPropertyParserRequest.validate(fuzzyParserAdditionalRequest)) {
            return;
        }
        DSBoundSql boundSql = fuzzyParserAdditionalRequest.getBoundSql();
        Object parameterObject = boundSql.parameterObject();
        List<ParameterMapping> parameterMappings = boundSql.parameterMappings();
        List<UpdateSet> updateSets = update.getUpdateSets();
        if (CollectionUtils.isEmpty(updateSets) || parameterObject == null) {
            return;
        }
        try {
            // process update set
            processUpdateSetFuzzy(updateSets, parameterMappings, parameterObject, fuzzyParserAdditionalRequest);
        } catch (Exception e) {
            Metrics.REGISTRY.timer(FUZZY_QUERY_ENCRYPT_UPDATE
                .withTag(FUZZY_QUERY_ENCRYPT_EXCEPTION,
                    Optional.of(e.getClass()).map(Class::getSimpleName).orElse(UNKNOWN)))
                .record(getDuration(currentStartTime));
            return;
        }
        Metrics.REGISTRY.timer(FUZZY_QUERY_ENCRYPT_UPDATE)
            .record(getDuration(currentStartTime));
    }

    private void processUpdateSetFuzzy(List<UpdateSet> updateSets, List<ParameterMapping> parameterMappings,
        Object parameterObject, FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        // 需要添加的分词列信息
        List<AddColumnInfo> addColumnInfos = new ArrayList<>();

        for (UpdateSet updateSet : updateSets) {
            Column column = updateSet.getColumns().get(0);
            Expression expression = updateSet.getExpressions().get(0);
            if (!(expression instanceof JdbcParameter)) {
                continue;
            }
            JdbcParameter jdbcParameter = (JdbcParameter)expression;
            Integer parameterIndex = jdbcParameter.getIndex();
            if (parameterIndex == null) {
                log.warn("ParameterIndex for update set is null, updateSet: {}", JsonUtils.toJsonString(updateSet));
                continue;
            }

            ParameterMapping parameterMapping = parameterMappings.get(parameterIndex - 1);
            String propertyName = parameterMapping.getProperty();

            // 判断是否需要处理
            Field propertyLocatedField = ParameterPropertyUtils.getPropertyLocatedField(fuzzyParserAdditionalRequest,
                parameterMapping, parameterObject);
            if (propertyLocatedField == null) {
                // 无Field信息
                continue;
            }
            SecurityFuzzyQueryField securityFuzzyQueryField =
                propertyLocatedField.getAnnotation(SecurityFuzzyQueryField.class);
            if (securityFuzzyQueryField == null) {
                // 跳过无SecurityFuzzyQueryField注解的属性
                continue;
            }
            // 获取字段取值
            String content = SecurityFuzzyQueryHandler.buildQueryContent(propertyName, parameterMapping,
                parameterObject, fuzzyParserAdditionalRequest);
            if (StringUtils.isBlank(content)) {
                continue;
            }

            // 分词加密
            String encryptAlgorithm = securityFuzzyQueryField.encryptAlgorithm();
            int divideLength = securityFuzzyQueryField.divideLength();
            String encryptedContent =
                SecurityFuzzyQueryHandler.encryptWithDividing(content, encryptAlgorithm, divideLength);

            if (StringUtils.isBlank(encryptedContent) || content.equals(encryptedContent)) {
                // 跳过加密失败数据的模糊查询条件
                log.warn(SKIP_THE_INVALID_ENCRYPTED_CONTENT, encryptedContent);
                return;
            }

            // 维护新增列的相关信息
            AddColumnInfo addColumnInfo = new AddColumnInfo();
            addColumnInfo.setContent(encryptedContent);
            addColumnInfo.setParameterMappingIndex(parameterIndex);
            addColumnInfo
                .setColumnName(SecurityFuzzyQueryHandler.buildDivideColumnName(securityFuzzyQueryField, column));
            addColumnInfos.add(addColumnInfo);
        }
        // 插入 分词列数据
        if (CollectionUtils.isNotEmpty(addColumnInfos)) {
            log.info("Update secret_divide columns for fuzzy query, {}",
                JsonUtils.toJsonString(addColumnInfos));
            for (AddColumnInfo addColumnInfo : addColumnInfos) {
                UpdateSet addUpdateSet = new UpdateSet();
                // 插入列名
                ArrayList<Column> columns = new ArrayList<>();
                columns.add(new Column(addColumnInfo.columnName));
                addUpdateSet.setColumns(columns);
                // 插入列取值
                ArrayList<Expression> expressions = new ArrayList<>();
                expressions.add(new StringValue(addColumnInfo.getContent()));
                addUpdateSet.setExpressions(expressions);
                // 插入updateSets
                updateSets.add(addUpdateSet);
            }
        }
    }

    /**
     * 遍历 PlainSelect 将like中需要模糊查询的字段替换成 加密的分词数据
     */
    private void processPlainSelect(PlainSelect plainSelect,
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        Expression whereExpression = plainSelect.getWhere();
        if (whereExpression == null) {
            return;
        }
        DSBoundSql boundSql = fuzzyParserAdditionalRequest.getBoundSql();
        if (boundSql == null) {
            return;
        }
        Object parameterObject = boundSql.parameterObject();
        List<ParameterMapping> parameterMappings = boundSql.parameterMappings();
        if (parameterObject == null || CollectionUtils.isEmpty(parameterMappings)) {
            return;
        }
        // 需要删除的占位符映射关系
        Set<Integer> removedMappingIndexSet = new HashSet<>();
        whereExpression.accept(new ExpressionVisitorAdapter() {
            @Override
            public void visit(LikeExpression likeExpression) {
                processLikeExpression(likeExpression, parameterMappings, parameterObject, removedMappingIndexSet,
                    fuzzyParserAdditionalRequest);
            }
        });
        // 移除ParameterMapping
        if (CollectionUtils.isNotEmpty(removedMappingIndexSet)) {
            List<ParameterMapping> newParameterMappings = new ArrayList<>();
            for (int i = 0; i < parameterMappings.size(); i++) {
                if (removedMappingIndexSet.contains(i + 1)) {
                    continue;
                }
                newParameterMappings.add(parameterMappings.get(i));
            }
            boundSql.parameterMappings(newParameterMappings);
        }
    }

    /**
     * 处理like表达式
     *
     * @param likeExpression like表达式
     * @param parameterMappings 参数映射
     * @param parameterObject 参数对象
     * @param removedMappingIndexSet 待删除的映射索引
     * @param fuzzyParserAdditionalRequest 额外请求参数
     */
    private void processLikeExpression(LikeExpression likeExpression, List<ParameterMapping> parameterMappings,
        Object parameterObject, Set<Integer> removedMappingIndexSet,
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        Expression leftExpression = likeExpression.getLeftExpression();
        if (!(leftExpression instanceof Column)) {
            // like左侧不为列名，则不处理
            return;
        }
        Column column = (Column)leftExpression;
        Expression rightExpression = likeExpression.getRightExpression();
        if (rightExpression instanceof JdbcParameter) {
            // 处理预编译参数
            processJdbcParameter(likeExpression, parameterMappings, parameterObject, removedMappingIndexSet,
                (JdbcParameter)rightExpression, column, fuzzyParserAdditionalRequest);
        } else if (rightExpression instanceof Function) {
            // 处理函数
            processFunction(likeExpression, parameterMappings, parameterObject, removedMappingIndexSet,
                (Function)rightExpression, column, fuzzyParserAdditionalRequest);
        }
        // else: like右侧不为?（预编译占位符），则不处理
    }

    /**
     * 处理like右侧的function
     *
     * @param likeExpression like表达式
     * @param parameterMappings 参数映射
     * @param parameterObject 参数对象
     * @param removedMappingIndexSet 待删除的映射索引
     * @param rightExpression 右侧的函数表达式
     * @param column 列名
     * @param fuzzyParserAdditionalRequest 额外请求参数
     */
    private void processFunction(LikeExpression likeExpression, List<ParameterMapping> parameterMappings,
        Object parameterObject, Set<Integer> removedMappingIndexSet, Function rightExpression, Column column,
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        String functionName = rightExpression.getName();
        if (StringUtils.isBlank(functionName)) {
            return;
        }
        if (!QUERY_FUNCTION_NAME_SET.contains(functionName)) {
            return;
        }
        // 处理 concat函数，eg: concat('%', ?, '%')
        ExpressionList parameters = rightExpression.getParameters();
        if (parameters == null) {
            return;
        }
        List<Expression> expressions = parameters.getExpressions();
        if (CollectionUtils.isEmpty(expressions)) {
            return;
        }
        List<Tuple<Integer, Tuple<Integer, String>>> modifiedExpressions = new ArrayList<>();
        for (int i = 0; i < expressions.size(); i++) {
            Expression expression = expressions.get(i);
            if (!(expression instanceof JdbcParameter)) {
                continue;
            }
            // encrypt and modify column
            Tuple<Integer, String> result = encryptAndModifyColumn(likeExpression, parameterMappings,
                parameterObject, column, (JdbcParameter)expression, fuzzyParserAdditionalRequest);
            if (result == null) {
                return;
            }
            // 将like右侧 预编译参数 替换为固定的参数
            modifiedExpressions.add(new Tuple<>(i, result));
        }
        if (CollectionUtils.isNotEmpty(modifiedExpressions)) {
            for (Tuple<Integer, Tuple<Integer, String>> modifiedExpression : modifiedExpressions) {
                Tuple<Integer, String> result = modifiedExpression.second();
                // 标记原占位符的映射参数为待删除
                removedMappingIndexSet.add(result.first());
                // 将 函数中的 预编译参数 替换为固定的加密参数
                expressions.set(modifiedExpression.first(), new StringValue(result.second()));
            }
        }
    }

    /**
     * 处理like右侧的JdbcParameter
     *
     * @param likeExpression like表达式
     * @param parameterMappings 参数映射
     * @param parameterObject 参数对象
     * @param removedMappingIndexSet 待删除的映射索引
     * @param rightExpression 右侧的预编译参数
     * @param column 列名
     * @param fuzzyParserAdditionalRequest 额外请求参数
     */
    private void processJdbcParameter(LikeExpression likeExpression, List<ParameterMapping> parameterMappings,
        Object parameterObject, Set<Integer> removedMappingIndexSet, JdbcParameter rightExpression, Column column,
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        Tuple<Integer, String> result =
            encryptAndModifyColumn(likeExpression, parameterMappings, parameterObject, column, rightExpression,
                fuzzyParserAdditionalRequest);
        if (result == null) {
            return;
        }

        // 将like右侧 预编译参数 替换为固定的加密参数
        likeExpression.setRightExpression(new StringValue(result.second()));

        // 标记原占位符的映射参数为待删除
        removedMappingIndexSet.add(result.first());
    }

    /**
     * 若有注解，则分词加密，并修改列为分词所在列（查询用）
     *
     * @param likeExpression like表达式
     * @param parameterMappings 参数映射
     * @param parameterObject 参数对象
     * @param column 列名
     * @param jdbcParameter 预编译参数
     * @param fuzzyParserAdditionalRequest 额外请求参数
     * @return Tuple<Integer, String>: parameterIndex, encryptedContent
     */
    private Tuple<Integer, String> encryptAndModifyColumn(LikeExpression likeExpression,
        List<ParameterMapping> parameterMappings, Object parameterObject, Column column, JdbcParameter jdbcParameter,
        FuzzyParserAdditionalRequest fuzzyParserAdditionalRequest) {
        Integer parameterIndex = jdbcParameter.getIndex();
        if (parameterIndex == null) {
            log.warn("Parsing where expression encounter null parameterIndex: {}",
                JsonUtils.toJsonString(likeExpression));
            return null;
        }
        ParameterMapping parameterMapping = parameterMappings.get(parameterIndex - 1);
        String propertyName = parameterMapping.getProperty();
        // 判断是否需要处理
        Field propertyLocatedField = ParameterPropertyUtils.getPropertyLocatedField(fuzzyParserAdditionalRequest,
            parameterMapping, parameterObject);
        if (propertyLocatedField == null) {
            // 无Field信息
            return null;
        }
        SecurityFuzzyQueryField securityFuzzyQueryField =
            propertyLocatedField.getAnnotation(SecurityFuzzyQueryField.class);
        if (securityFuzzyQueryField == null) {
            // 跳过无SecurityFuzzyQueryField注解的属性
            return null;
        }
        // 获取字段取值
        String content = SecurityFuzzyQueryHandler.buildQueryContent(propertyName, parameterMapping,
            parameterObject, fuzzyParserAdditionalRequest);
        if (StringUtils.isBlank(content)) {
            return null;
        }

        // 分词加密
        String encryptAlgorithm = securityFuzzyQueryField.encryptAlgorithm();
        int divideLength = securityFuzzyQueryField.divideLength();
        String encryptedContent =
            SecurityFuzzyQueryHandler.encryptWithDividing(content, encryptAlgorithm, divideLength, true);

        if (StringUtils.isBlank(encryptedContent) || content.equals(encryptedContent)) {
            // 跳过加密失败数据的模糊查询条件
            log.warn(SKIP_THE_INVALID_ENCRYPTED_CONTENT, encryptedContent);
            return null;
        }

        // 修改原列信息为分词所在表及列
        modifyTableColumn(securityFuzzyQueryField, column);
        return new Tuple<>(parameterIndex, encryptedContent);
    }

    /**
     * 修改原列信息为分词所在表及列
     * 
     * @param securityFuzzyQueryField the annotation of SecurityFuzzyQueryField
     * @param column the column
     */
    private static void modifyTableColumn(SecurityFuzzyQueryField securityFuzzyQueryField, Column column) {
        // 改写表名
        String divideTable = securityFuzzyQueryField.divideTable();
        if (StringUtils.isNotBlank(divideTable)) {
            Table table = column.getTable();
            table.setName(divideTable);
            column.setTable(table);
        }
        // 改写列名
        String divideColumn = SecurityFuzzyQueryHandler.buildDivideColumnName(securityFuzzyQueryField, column);
        column.setColumnName(divideColumn);
    }

    @Override
    public void setProperties(Properties properties) {
        // empty method
    }

    @Override
    public int order() {
        return -100;
    }

    /**
     * additional request arguments for fuzzy parse
     */
    @NoArgsConstructor
    @AllArgsConstructor
    public static class FuzzyParserAdditionalRequest implements IPluginPropertyParserRequest {
        /**
         * boundSql for modifying
         */
        private DSBoundSql boundSql;
        /**
         * index of parameterMappings
         */
        @Getter
        private Integer parameterMappingIndex = 0;
        /**
         * mybatis configuration for parsing
         */
        private Configuration configuration;
        /**
         * delayed add columns for multi-expression insert sql
         */
        @Getter
        private List<Column> delayedAddColumns = new ArrayList<>();

        public FuzzyParserAdditionalRequest(DSBoundSql boundSql, Configuration configuration) {
            this.boundSql = boundSql;
            this.configuration = configuration;
        }

        @Override
        public DSBoundSql getBoundSql() {
            return boundSql;
        }

        @Override
        public Configuration getConfiguration() {
            return configuration;
        }

    }

    /**
     * 添加的列相关信息
     */
    @Data
    private static class AddColumnInfo {
        /**
         * 内容
         */
        private String content;

        /**
         * 列名
         */
        private String columnName;

        /**
         * 预编译占位符 索引（从1开始计数）
         */
        private int parameterMappingIndex;
    }

    /**
     * get the duration since start time
     */
    private static Duration getDuration(Instant startTime) {
        return Duration.between(startTime, Instant.now());
    }
}
